# -*- coding: utf-8 -*-

"""
 *
 * - Ime
 *
 *  - kratki opis
 *
 * Autori: Koki007
 * Verzija: 0.1
 * Datum: 04.01.2011
 *
"""

import pygame, math
from animacija import cAnimacija
from tajmer import cTajmer, cTask_tajmer

import os

##################
# - predstavlja fizicki objekt na nekoj lokaciji u igri, te se moze prevjeriti da li je u dodiru sa drugim objektima, i izvisiti neku reakciju ako jest
# osnovni objekt - sadrzi samo sliku, koordinate i pravokutnik za provjeru sudara, generiran iz slike objekta
class cObjekt():

    # !KOMENTAR
    def __init__(self, engine, ime, x, y, animacija = False, colorkey = 0):
        print "Dodavanje objekta: " + ime + "; Pozicija:" , str(x), str(y)
        self.engine = engine
        
        self.pozicija_x = x
        self.pozicija_y = y
        self.kut = 0

        self.animacija = animacija
        self.AI = 0

        if self.animacija:
            self.slika_za_crtanje = cAnimacija(ime)
        else:
            self.slika = pygame.image.load(ime).convert()
            # slika koja se crta u trenutku crtanja
            self.slika_za_crtanje = self.slika
            if colorkey: self.slika_za_crtanje.set_colorkey(colorkey)

        self.collision_rect = self.slika_za_crtanje.get_rect(center = (self.pozicija_x,self.pozicija_y))
        

        # stanja
        self.vidljiv = True # da li je objekt vidljiv
        self.interaktivan = True # da li drugi objekti provjervaju sudar s njim ***TODO
        # implementirati self.cvrst
        self.cvrst = True # da li fizika djeluje na njega, odnosno da li npr. igrac moze proci kroz njega ili ne
        self.izbrisan = False # da li se objekt moze izbrisati iz liste

        # titranje
        self.titra = False # da li objekt titra
        self.titranje_tajmer  = 0 # cTajmer
        self.duljina_titranja_tajmer = 0 # cTajmer
        
        # ako je postavljen fading ova varijabla sadrzi objekt cTajmer klase koji odbrojava to "fejdanje"
        self.fading = 0

        # varijable poput koliko jabuka/zvijezdica/mane igrac ima nece biti hardcodirane nego ce sve takve varijable koje su vezane uz lik biti spremljene u ovaj dict
        # ovo je sto zbog vece fleksibilnsti iterakcije izmedu objekata, kad jednom bude upogonjen game editor
        self.korisnikove_varijable = {}
        
        # 
        self.task_tajmer = cTask_tajmer()

        # objekti imaju svoje interaktivne grupe po kojima se moze kontrolirati tko s kim moze 
        self.interaktivna_grupa = ""
        self.interaktivna_lista = {}
        
        # kao sto i samo ime govori, ovdje ce biti pohranjeni pygame zvucni objekti, koji ce se moci metodama u ovom objektu pozivati prema referentom imenu
        self.zvukovi = {}
        pass


    # !KOMENTAR
    def azuriraj(self):
        if self.animacija: self.slika_za_crtanje.azuriraj()
        if self.AI: self.AI.azuriraj()
        
        #odraduje titranje objekta
        if self.titra:
            if self.titranje_tajmer.azuriraj():
                if self.vidljiv: self.vidljiv = 0
                else: self.vidljiv = 1
            if self.duljina_titranja_tajmer.azuriraj():
                self.titra = 0
                self.vidljiv = 1

        # odraduje "izbljedivanje"
        if self.fading:
            self.fading.azuriraj()
            proteklo = self.fading.je_proteklo_posto_vremena()
            proteklo = (100 - proteklo) / 100 * 255 # kako vrijeme prolazi da vrijednost pada (100 - proteklo), te da tu vrijednost rangira od 0 do 255 ( / 100 * 255) - potrbno za fade() funkciju 
            self.postavi_transparentnost(proteklo)
            # ako objekt "nestane" do kraja moze se izbrisati
            if proteklo <= 1:
                self.izbrisan = True

        self.task_tajmer.azuriraj()
        pass


    # !KOMENTAR
    def crtaj(self):
        if self.vidljiv:
            if self.animacija: return self.slika_za_crtanje.crtaj() #return pygame.transform.rotate( self.slika_za_crtanje.crtaj(), self.kut)
            return self.slika_za_crtanje
        else: return False


    # vraca poziciju i velicinu pravokutnika za provjeru sudara
    def provjeri_collison_rect(self):
        return self.slika_za_crtanje.get_rect(center = (self.pozicija_x,self.pozicija_y))


    # !KOMENTAR
    def povezi_sa_mapom(self, mapa,  sloj):
        self.mapa = mapa
        self.sloj = sloj

    # !KOMENTAR
    def postavi_na_koordinate(self, x, y):
        self.pozicija_x = x
        self.pozicija_y = y

    # !TODO
    #def je_ziv(self):
     #   return True

    # vraca x ili y poziciju objekta
    def provjeri_poziciju_x(self):
        return self.pozicija_x

    def provjeri_poziciju_y(self):
        return self.pozicija_y

    def provjeri_koordinate(self):
        return self.pozicija_x, self.pozicija_y

    def dodaj_AI(self, AI):
        self.AI = AI
        self.AI.povezi_sa_objektom(self)

    # objekt titra zadanom brzinom i duzinom trajanja
    def titraj(self, brzina_titranja, trajanje):
        self.titranje_tajmer = cTajmer(brzina_titranja, 1)
        self.duljina_titranja_tajmer = cTajmer(trajanje)
        self.titra = 1
        pass

    # ime na hrvatskom?? O.o
    # objekt izbljeduje te na kraju nestane
    def fade_out(self, trajanje):
        if not self.fading: self.fading = cTajmer(trajanje)
        pass

    # od 0 do 255 - pyGame ne podrzava to sa slikama s perpixel vrijednostima!!!
    def postavi_transparentnost(self, vrijednost):
        self.slika_za_crtanje.set_alpha(vrijednost)
        

    # true ako se objekt moze izbrisati iz mape
    def je_izbrisan(self):
        return self.izbrisan

    def izbrisi(self):
        self.izbrisan = True

    # ako je argument false objekt se ne crta
    def postavi_vidljivost(self, opcija):
        self.vidljiv = opcija

    def odgovor_na_sudar(self, objekt):
        pass

    # provjerava da li je objekt interaktivan sa argumentom-objektom
    def provjeri_interaktivnost(self, objekt):
        if objekt.interaktivna_grupa in self.interaktivna_lista:
            return self.interaktivna_lista[objekt.interaktivna_grupa]
        
        elif self.interaktivna_grupa in objekt.interaktivna_lista:
            return objekt.interaktivna_lista[self.interaktivna_grupa]

        # ako nije zadano ponasanje onda vrati da su interaktivni
        else: return True

    #
    def postavi_interaktivnost(self, interaktivna_grupa, vrijednost):
        self.interaktivna_lista[interaktivna_grupa] = vrijednost
        pass
    
    def postavi_grupu(self, ime):
        self.interaktivna_grupa = ime
        
    def je_grupa(self, ime):
        if self.interaktivna_grupa == ime: return True
        return False
    #
    def postavi_interaktivnost_vremenski(self):
        pass


    def dodaj_zvuk(self, ime, datoteka):
        self.zvukovi[ime] = pygame.mixer.Sound(datoteka)
        pass
        
    def sviraj_zvuk(self, ime):
        self.zvukovi[ime].play()
        pass


###################################
# - predstavlja pokretni fizicki objekt na nekoj lokaciji u igri, te se moze prevjeriti da li je u dodiru sa drugim objektima, i izvisiti neku reakciju ako jest
class cPokretni_objekt(cObjekt):

    def __init__(self, engine, ime, x, y, animacija = False, colorkey = 0): 
        cObjekt.__init__(self, engine, ime, x, y, animacija, colorkey)

        self.inercija_x = True # da li objekt ima inerciju kretanja po x osi
        self.inercija_y = True # da li objekt ima inerciju kretanja po y osi

        self.brzina_x = 0 # trenutna brzina objekta po x osi
        self.brzina_y = 0 # trenutna brzina objekta po y osi
        
    def azuriraj(self):
        cObjekt.azuriraj(self)

        # ako je objekt podlozan inerciji pomakni ga svaki loop 
        # ako je nakon pomaka doslo do sudara objekti se vracaju na staru poziciju
        # TODO: staviti samo jednu provjeru nakon oba pomaka?
        if self.inercija_x and self.inercija_y: 
            self.pomakni_za_vektor(self.brzina_x, self.brzina_y)

        elif self.inercija_x: 
            self.pomakni_za_vektor(self.brzina_x, 0)
            
        elif self.inercija_y: 
            self.pomakni_za_vektor(0, self.brzina_y)
            
            
    # pomakni objekt u smjeru nekog vektora
    def pomakni_za_vektor(self, x, y):
        
        stari_x = self.pozicija_x
        stari_y = self.pozicija_y
        
        self.pozicija_x += x # 
        self.pozicija_y += y #

        self.provjera_sudara(stari_x, stari_y)


    # ubrzaj objekt u smjeru nego vektora
    def ubrzaj_za_vektor(self, x, y):
        self.brzina_x += x # trenutna brzina objekta po x osi
        self.brzina_y += y # trenutna brzina objekta po y osi

    # ubrzaj u smjeru prema kojem objekta gleda, zamaknuto za zadani broj (kut) stupnjeva
    def ubrzaj_za_relativni_kut(self, ubrzanje, kut = 0):
        kut += self.kut
        x, y = self.izracunaj_vektor(kut)
        self.ubrzaj_za_vektor(x * ubrzanje, y * ubrzanje)
        pass

        
    # pomakni objekt na zadane koordinate
    def postavi_poziciju(self, x, y):
        stari_x = self.pozicija_x
        stari_y = self.pozicija_y
        
        self.pozicija_x = x # 
        self.pozicija_y = y #

        self.provjera_sudara(stari_x, stari_y)


    # provjera sudara sa svim objektima u mapi u kojoj se nalazi objekt
    def provjera_sudara(self, stari_x = None, stari_y = None, stari_kut = None ):
        # uzima listu svih objekata iz mape
        for objekt in self.mapa.lista_svih_interaktivnih_objekata():
            # da ne provjerava sudar sa samim sobom
            if id(self) != id(objekt):
                # provjeri samo ako su objekti interaktivni
                if objekt.interaktivan and self.interaktivan:
                    # provjeri samo ako taj objekt moze odjelovati na objekt koji provjerava sudar
                    if self.provjeri_interaktivnost(objekt) == True:

                            # uzimanje collison recteva
                            objekt_rect = objekt.provjeri_collison_rect()
                            ja_rect = self.provjeri_collison_rect()

                            #ako su rectevi sudareni za oba dva objekta pozivaj metode za odgovor na sudar
                            if ja_rect.colliderect(objekt_rect):
                                if stari_x != None: self.pozicija_x = stari_x
                                if stari_y != None: self.pozicija_y = stari_y
                                if stari_kut != None: 
                                    self.kut = stari_kut
                                    self.slika_za_crtanje = pygame.transform.rotate( self.slika, stari_kut)
                                    
                                self.odgovor_na_sudar(objekt)
                                objekt.odgovor_na_sudar(self)
                                return objekt

        #ako nije sudaren ni sa jednim objektom iz popisa:
        return False

    def odgovor_na_sudar(self, objekt):
        
        pass 
        
    # BUG: staviti da provjeri je li u sudaru nakon rotiranja
    # TODO: ako je objekt neinteraktivni izbaciti provjeru
    def postavi_kut(self, kut):
        stari_kut = self.kut
        self.kut = kut
        self.slika_za_crtanje = pygame.transform.rotate( self.slika, self.kut)
        self.provjera_sudara(stari_kut = stari_kut)
        pass

    def provjeri_kut(self):
        return self.kut

    #racuna iznost kuta za koji objekt "gleda" u zadane koordinate
    #dobiva polozaj izvora(objekta), i polozaj druge tocke, te vraca kut izmedu pravca objekt->druga_tocka naspram x osi
    def izracunaj_kut(self, x, y):
        udaljenost_x = x - self.pozicija_x
        udaljenost_y = y - self.pozicija_y
        kut = math.atan2(-udaljenost_y,udaljenost_x)
        return math.degrees(kut)
        
    def izracunaj_vektor(self, kut):
        kut = math.radians( kut ) # pretvori stupnjeve u radijane jer math.sin/cos metoda primaju radijane za argument
        x = math.cos(kut)
        y = -math.sin(kut)
        return x, y



# ######################################################################
# - predstavlja pokreni fizicki objekt na nekoj lokaciji u igri, te se moze prevjeriti da li je u dodiru sa drugim objektima, i izvisiti neku reakciju ako jest
# - posebnost ovo klase je sto se slika objekta zrcali na lijevo ili desno 
# klasa za pokretne sidescroll objekte - nasljeduje cObjekte (pa tako i sve njene attribute i metode)
# TODO: neka nasljeduje cPokretni_objekt (kad bude gotova) i neka koristi njenje metode za kretanje
# !BUG1: "pregrubo" skakanje?? (zbog velike razlike u poziciji nakon prvog framea skoka)
# !BUG2: razmak izmedu objekata nakon sudara i odskok kod sudaru u tlo nakon skoka (jer se vraca na staru poziciju a ne na najblizu mogucu k objektu s kojim se je sudario )
class cPokretni_sidescroll_objekt(cObjekt):

    def __init__(self, engine, ime, x, y, animacija = False, colorkey = 0):

        cObjekt.__init__(self,engine ,ime, x, y, animacija, colorkey)

        if self.animacija:
            self.slika_desno = cAnimacija(ime, 0)
            self.slika_lijevo = cAnimacija(ime, 1)

        else:
            # slika koja se prikazuje kada je objekt okrenuti prema desno
            self.slika_desno = self.slika_za_crtanje

            # slika koja se prikazuje kada je objekt okrenuti prema lijevo
            self.slika_lijevo = pygame.transform.flip(self.slika_desno, True, False)

        self.gravitacija = 0.5
        self.brzina = 3 # brzina kojom se krece po x osi
        self.na_tlu = 0 # prati da li je objekt na tlu

        # brzina kojom se objekt krece po y osi
        self.brzina_y = 0

        # pocetna brzina koju objekt dobi prilikom skoka
        self.snaga_skoka = -12
        pass

    # sve sto se treba odraditi svaki frame/game_loop
    def azuriraj(self):
        cObjekt.azuriraj(self)
        # gravitacija
        self.brzina_y += self.gravitacija # y brzina se mijenja za iznos gravitacije koja utjece na nju
        self.pozicija_y += self.brzina_y # pozicija se mijenja na temelju y brzine kojom se igrac krece
        # nakon promjene pozicije provjera je li objekt u sudaru sa necim, ako je:
        if self.provjera_sudara():
            self.pozicija_y -=  self.brzina_y #objekt se vraca na staru poziciju
            self.brzina_y = 0 # ne krece se vise po y osi
            self.na_tlu = 1 # i postavlja se varijabla da je na tlu
        else: self.na_tlu = 0


    def pomakni_desno(self):
        # pomakni desno
        self.pozicija_x += self.brzina
        
        # ako je objekt nakon pomaka u sudaru, vrati ga natrag
        objekt = self.provjera_sudara()
        if objekt:
            if objekt.cvrst:
                self.pozicija_x -= self.brzina

        self.slika_za_crtanje = self.slika_desno
        
        #print self.animacija
        if self.animacija: self.slika_za_crtanje.pokreni_animaciju("trcanje", 10, 0) # MAKNUTI hardkodiranu vrijednost O.o
        

    def pomakni_lijevo(self):
        self.pozicija_x -= self.brzina

        # ***TODO: srediti da objekt moze doc tik do drugog objekta
        # ako je objekt nakon pomaka u sudaru, vrati ga natrag
        objekt = self.provjera_sudara()
        if objekt:
            if objekt.cvrst:self.pozicija_x += self.brzina

        self.slika_za_crtanje = self.slika_lijevo

        #print self.animacija
        if self.animacija: self.slika_za_crtanje.pokreni_animaciju("trcanje", 10, 0) # MAKNUTI hardkodiranu vrijednost O.o

    def skoci(self):
        if self.na_tlu: self.brzina_y = self.snaga_skoka

    # provjera sudara sa svim objektima u mrezi u kojoj se nalazi objekt
    def provjera_sudara(self):
        # uzima listu svih objekata iz mreze
        for objekt in self.mapa.lista_svih_interaktivnih_objekata():
            if objekt.interaktivan and self.interaktivan:

                if self.provjeri_interaktivnost(objekt) == True:
                    # provjerava sa svim objektima osim sa samim sobom
                    if id(self) != id(objekt):

                            objekt_rect = objekt.provjeri_collison_rect()
                            ja_rect = self.provjeri_collison_rect()

                            if ja_rect.colliderect(objekt_rect):
                                self.odgovor_na_sudar(objekt)
                                objekt.odgovor_na_sudar(self)
                                return objekt

        #ako nije sudaren ni sa jednim objektom iz popisa:
        return False


    def promjeni_gravitaciju(self, nova_vrijednost):
        self.gravitacija = nova_vrijednost

    def postavi_transparentnost(self, vrijednost):
        self.slika_desno.set_alpha(vrijednost)
        self.slika_lijevo.set_alpha(vrijednost)


